package com.zmj.sy.mom.srv.aps.utils.algorithm;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zmj.sy.mom.srv.aps.bean.bo.*;
import com.zmj.sy.mom.srv.aps.bean.entity.base.BaseEntity;
import com.zmj.sy.mom.srv.aps.bean.entity.order.ApsDetailWeekday;
import com.zmj.sy.mom.srv.aps.bean.entity.order.ApsWorkDetail;
import com.zmj.sy.mom.srv.aps.bean.entity.order.ApsWorkOrder;
import com.zmj.sy.mom.srv.aps.bean.entity.resource.Group;
import com.zmj.sy.mom.srv.aps.bean.entity.resource.Station;
import com.zmj.sy.mom.srv.aps.mapper.ApsDetailWeekdayMapper;
import com.zmj.sy.mom.srv.aps.mapper.ApsWorkDetailMapper;
import com.zmj.sy.mom.srv.aps.mapper.ApsWorkOrderMapper;
import com.zmj.sy.mom.srv.aps.utils.ApsUtils;
import com.zmj.sy.mom.srv.aps.utils.SyExceptionUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
@Slf4j
public class ApsForwardScheduling implements ApsAlgorithm {

    private final ApsWorkOrderMapper apsWorkOrderMapper;
    private final ApsDetailWeekdayMapper apsDetailWeekdayMapper;
    private final ApsWorkDetailMapper apsWorkDetailMapper;

    @Transactional
    @Override
    public void scheduleTask(ApsFactoryCache apsFactoryCache, ApsOrderCache apsOrderCache) {

        Map<Integer, Map<Integer, List<ApsTask>>> idPhaseSeqMap = apsOrderCache.getWorkOrderIdPhaseSeqMap();

        LocalDateTime start = null;
//        Map<Integer, StationGroup> stationGroupMap = apsFactoryCache.getStationGroups().stream().collect(Collectors.toMap(StationGroup::getStationId, e -> e));
        Map<Integer, Group> groupMap = apsFactoryCache.getGroups().stream().collect(Collectors.toMap(Group::getId, Function.identity()));
        Map<Integer, ApsWorkOrder> workOrderMap = apsOrderCache.getWorkOrders().stream().collect(Collectors.toMap(ApsWorkOrder::getId, Function.identity()));
        Map<Integer, Station> stationIdMap = apsFactoryCache.getStations().stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));

        for (ApsTask currentTask : apsOrderCache.getTaskList()) {
            if (currentTask.getApsWorkDetail().getStatus() > 2) {
                continue;
            }

            start = null;

            // 1.1 校验前一道工序的完成时间,相同work_order_no, nxphase - 1
            Optional<Integer> max = idPhaseSeqMap.get(currentTask.getApsWorkOrderId()).keySet().stream().filter(e -> e < currentTask.getPhaseSeq()).max(Integer::compareTo);
            List<ApsTask> preTask = null;
            if(max.isPresent()){
                preTask = idPhaseSeqMap.get(currentTask.getApsWorkOrderId()).get(max.get());
            }
            if (preTask != null && !preTask.isEmpty()) {
                preTask.stream().filter(task -> task.getPlanEndDateTime() == null).findFirst().ifPresent(task -> {
                    throw SyExceptionUtils.e("[{}]的工序[{}]在工序[{}]之前", currentTask.getApsWorkDetail().getMaterName(), currentTask.getApsWorkDetail().getPhaseCode(), task.getApsWorkDetail().getPhaseCode());
                });

                List<ApsTask> tasks = preTask.stream().filter(e -> e.getConsumerStatus().equals(1) || e.getConsumerStatus().equals(2)).sorted(Comparator.comparing(ApsTask::getConsumerStatus).reversed()).collect(Collectors.toList());
                BigDecimal total = tasks.stream().map(e -> e.getApsWorkDetail().getTotalCount()).reduce(BigDecimal::add).orElseThrow(() -> SyExceptionUtils.e("没有找到前一道工序的数量"));
                if (currentTask.getApsWorkDetail().getTotalCount().compareTo(total) > 0) {
                    throw SyExceptionUtils.e("数量不足");
                }

                BigDecimal remainder = currentTask.getApsWorkDetail().getTotalCount();
                List<ApsTask> newPreTask = new ArrayList<>();
                for (ApsTask task : tasks) {
                    if (remainder.compareTo(BigDecimal.ZERO) <= 0) {
                        break;
                    }
                    BigDecimal count = task.getApsWorkDetail().getTotalCount();
                    newPreTask.add(task);
                    if (remainder.compareTo(count) > 0) {
                        task.setConsumerStatus(3);
                        remainder = remainder.subtract(count);
                    } else if (remainder.compareTo(count) == 0) {
                        task.setConsumerStatus(3);
                        task.setConsumerCount(task.getConsumerCount().add(remainder));
                        break;
                    } else {
                        task.setConsumerStatus(2);
                        task.setConsumerCount(task.getConsumerCount().add(remainder));
                        remainder = BigDecimal.ZERO;
                    }
                    task.setConsumerCount(task.getConsumerCount().add(remainder));
                }


                ApsTask maxPreTask = newPreTask.stream().max(Comparator.comparing(ApsTask::getPlanEndDateTime)).orElseThrow(() -> SyExceptionUtils.e("没有找到前一道工序的时间"));
                start = maxPreTask.getPlanEndDateTime();
            } else {
                // 1.2 如果没有前一道工序，校验子节点的完成时间
                List<ApsTask> optionalTask = apsOrderCache.getWorkOrderPidMap().get(currentTask.getApsWorkOrderId());

                if (CollUtil.isNotEmpty(optionalTask)) {

                    // 判断是否有未完成的子节点，如果有则抛出异常
                    optionalTask.stream().filter(task -> task.getPlanEndDateTime() == null).findFirst().ifPresent(task -> {
                        throw SyExceptionUtils.e("子节点未完成");
                    });

                    // ---- 修复BUG - 开始 ---
                    Map<Integer, List<ApsTask>> childrenNodeMap = optionalTask.stream().collect(Collectors.groupingBy(ApsTask::getApsWorkOrderId));
                    for (Map.Entry<Integer, List<ApsTask>> entry : childrenNodeMap.entrySet()) {
                        Integer maxPhaseSeq = entry.getValue().stream().map(ApsTask::getPhaseSeq).max(Integer::compare).orElseThrow(() -> SyExceptionUtils.e("没有找到最大工序"));
                        List<ApsTask> maxPhaseSeqList = entry.getValue().stream().filter(e -> e.getApsWorkDetail().getPhaseSeq().equals(maxPhaseSeq)).filter(e -> e.getConsumerStatus().equals(1) || e.getConsumerStatus().equals(2)).sorted(Comparator.comparing(ApsTask::getConsumerStatus).reversed()).collect(Collectors.toList());
                        BigDecimal total = maxPhaseSeqList.stream().map(e -> e.getApsWorkDetail().getTotalCount()).reduce(BigDecimal::add).orElseThrow(() ->
                                SyExceptionUtils.e("没有找到前一道工序的数量")
                        );
                        BigDecimal consumerCount = maxPhaseSeqList.stream().map(ApsTask::getConsumerCount).reduce(BigDecimal::add).orElseThrow(() -> SyExceptionUtils.e("没有找到前一道工序的数量"));
                        BigDecimal parentTotalCount = currentTask.getApsWorkDetail().getTotalCount();
                        BigDecimal partCount = workOrderMap.get(maxPhaseSeqList.get(0).getApsWorkOrderId()).getPartCount();
                        BigDecimal sourceTotal = total.subtract(consumerCount);
                        BigDecimal koujianTotal = parentTotalCount.multiply(partCount);
                        BigDecimal remainder = sourceTotal.subtract(koujianTotal);

                        System.out.println(String.format("总数量: %s, 已消耗: %s, 父节点数量: %s, 单件数量: %s,需求数: %s, 剩余数: %s", total, consumerCount, parentTotalCount, partCount, koujianTotal, sourceTotal));

                        if (remainder.compareTo(BigDecimal.ZERO) < 0) {
                            throw SyExceptionUtils.e("数量不足");
//                            break;
                        }

                        remainder = koujianTotal;

                        List<ApsTask> newPreTask = new ArrayList<>();
                        for (ApsTask task : maxPhaseSeqList) {
                            BigDecimal count = task.getApsWorkDetail().getTotalCount().subtract(task.getConsumerCount());
                            newPreTask.add(task);
                            if (remainder.compareTo(count) > 0) {
                                task.setConsumerStatus(3);
                                remainder = remainder.subtract(count);
                                task.setConsumerCount(task.getConsumerCount().add(count));
                            } else if (remainder.compareTo(count) == 0) {
                                task.setConsumerStatus(3);
                                remainder = remainder.subtract(count);
                                task.setConsumerCount(task.getConsumerCount().add(count));
                            } else {
                                task.setConsumerStatus(2);
                                task.setConsumerCount(task.getConsumerCount().add(remainder));
                                remainder = BigDecimal.ZERO;
                            }

                            if (remainder.compareTo(BigDecimal.ZERO) == 0) {
                                break;
                            }

//                            if(remainder.compareTo(BigDecimal.ZERO) < 0){
//                                throw SyExceptionUtils.e("数量不足");
//                            }
                        }

                        // 之后取子节点的最大结束时间作为开始时间
                        ApsTask task = newPreTask.stream().max(Comparator.comparing(ApsTask::getEndTime)).orElseThrow(() -> SyExceptionUtils.e("没有找到最大的子节点时间"));
                        if(start == null || start.isBefore(task.getEndTime())){
                            start = task.getEndTime();
                        }

                    }

                    // ---- 修复BUG - 结束 ---

                    /* 老代码

                    List<ApsTask> tasks = optionalTask.stream().filter(e -> e.getConsumerStatus().equals(1) || e.getConsumerStatus().equals(2)).sorted(Comparator.comparing(ApsTask::getConsumerStatus).reversed()).collect(Collectors.toList());
                    ApsTask maxTask = tasks.stream().max(Comparator.comparing(ApsTask::getPhaseSeq)).orElseThrow(() -> SyExceptionUtils.e("没有找到最大工序"));
                    BigDecimal total = tasks.stream().filter(e -> e.getPhaseSeq().equals(maxTask.getPhaseSeq())).map(e -> e.getApsWorkDetail().getTotalCount()).reduce(BigDecimal::add).orElseThrow(() -> SyExceptionUtils.e("没有找到前一道工序的数量"));
                    if (currentTask.getApsWorkDetail().getTotalCount().compareTo(total) > 0) {
                        throw SyExceptionUtils.e("数量不足");
                    }

                    BigDecimal remainder = currentTask.getApsWorkDetail().getTotalCount();
                    List<ApsTask> newPreTask = new ArrayList<>();
                    for (ApsTask task : tasks) {
                        if (remainder.compareTo(BigDecimal.ZERO) <= 0) {
                            break;
                        }
                        BigDecimal count = task.getApsWorkDetail().getTotalCount();
                        newPreTask.add(task);
                        if (remainder.compareTo(count) > 0) {
                            task.setConsumerStatus(3);
                            remainder = remainder.subtract(count);
                            task.setConsumerCount(count);
                        } else if (remainder.compareTo(count) == 0) {
                            task.setConsumerStatus(3);
                            remainder = BigDecimal.ZERO;
                            task.setConsumerCount(remainder);
                            break;
                        } else {
                            task.setConsumerStatus(2);
                            task.setConsumerCount(remainder);
                        }
                    }

                    // 之后取子节点的最大结束时间作为开始时间
                    ApsTask task = newPreTask.stream().max(Comparator.comparing(ApsTask::getEndTime)).orElseThrow(() -> SyExceptionUtils.e("没有找到最大的子节点时间"));
                    start = task.getEndTime();


                     */
                }
            }

            // 2.找工序对应的设备列表
            List<ApsMachine> machines = apsFactoryCache.getGroupCodeMachineMap().get(currentTask.getGroupCode());
            if (CollUtil.isEmpty(machines)) {
                throw SyExceptionUtils.e("没有找到工作组:[" + currentTask.getGroupCode() + "], 对应的工序:[" + currentTask.getPhaseCode() + "]");
            }

            // 3.把此工序在该列表中依次排产
            List<ApsDetailWeekdayWrap> l = new ArrayList<>();
            for (ApsMachine machine : machines) {
                if (start == null) {
                    start = apsOrderCache.getStartDateTime();
                }

//                if(currentTask.getPhaseCode().equals(apsFactoryCache.getPzCode())){
//                    start = start.plusSeconds(apsFactoryCache.getPzSecond());
//                }

                ApsDetailWeekdayWrap scheduling = ApsUtils.scheduling(apsFactoryCache, apsOrderCache, start, machine, currentTask, apsDetailWeekdayMapper, apsWorkDetailMapper);
                l.add(scheduling);
            }

            // 4.取出时间最靠前的一次排产结果
            ApsDetailWeekdayWrap calendarWrap = l.stream().min(Comparator.comparing(ApsDetailWeekdayWrap::getStartDateTime)).orElseThrow(() -> SyExceptionUtils.e("排产异常"));
            currentTask.getApsWorkDetail().setPlanStartDateTime(calendarWrap.getStartDateTime());
            currentTask.getApsWorkDetail().setPlanEndDateTime(calendarWrap.getEndDateTime());
            currentTask.setNextTask(calendarWrap.getNext());
            calendarWrap.getPrevious().setNextTask(currentTask);
            currentTask.getApsWorkDetail().setStationId(calendarWrap.getMachine().getId());
            currentTask.setStartMachineCalendar(calendarWrap.getStartMachineCalendar());
            currentTask.setAll(calendarWrap.getAll());
            currentTask.getApsWorkDetail().setStatus(2);


        }
//
//        for (ApsTask apsTask : apsOrderCache.getTaskList()) {
//            if(!apsTask.getConsumerCount().equals(apsTask.getApsWorkDetail().getTotalCount())){
//                System.out.println(String.format("%s, %s, %s, %s, %s", apsTask.getConsumerCount(), apsTask.getApsWorkDetail().getTotalCount(), apsTask.getApsWorkDetail().getMaterName(), apsTask.getApsWorkDetail().getMaterNo(), apsTask.getApsWorkDetail().getPhaseCode()));
//            }
//        }

        Map<Integer, List<ApsTask>> collect = apsOrderCache.getTaskList().stream().collect(Collectors.groupingBy(ApsTask::getApsWorkOrderId));

        Map<Integer, ApsWorkOrder> workOrderIdMap = apsOrderCache.getWorkOrders().stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));

        for (Map.Entry<Integer, List<ApsTask>> entry : collect.entrySet()) {

            LambdaUpdateWrapper<ApsWorkOrder> owd = Wrappers.lambdaUpdate();
            owd.eq(ApsWorkOrder::getId, entry.getKey());
            LocalDateTime planStartDateTime = entry.getValue().stream().min(Comparator.comparing(ApsTask::getPlanStartDateTime)).get().getPlanStartDateTime();
            LocalDateTime planEndDateTime = entry.getValue().stream().max(Comparator.comparing(ApsTask::getPlanEndDateTime)).get().getPlanEndDateTime();

            ApsWorkOrder apsWorkOrder = workOrderIdMap.get(entry.getKey());
            apsWorkOrder.setPlanStartDateTime(planStartDateTime);
            apsWorkOrder.setPlanEndDateTime(planEndDateTime);


            owd.set(ApsWorkOrder::getPlanStartDateTime, apsWorkOrder.getPlanStartDateTime());
            owd.set(ApsWorkOrder::getPlanEndDateTime, apsWorkOrder.getPlanEndDateTime());
            owd.set(ApsWorkOrder::getStatus, 1);
            apsWorkOrderMapper.update(null, owd);
        }


        apsOrderCache.setDetailWeekdays(new ArrayList<>());
        for (ApsTask task : apsOrderCache.getTaskList()) {
            ApsWorkDetail detail = task.getApsWorkDetail();

            Station station = stationIdMap.get(detail.getStationId());
//            StationGroup stationGroup = stationGroupMap.get(detail.getStationId());
            Group group = groupMap.get(station.getGroupId());
            detail.setGroupId(group.getId());
            detail.setGroupCode(group.getMyCode());


            LambdaUpdateWrapper<ApsWorkDetail> owd = Wrappers.lambdaUpdate();
            owd.eq(ApsWorkDetail::getId, detail.getId());
            owd.set(ApsWorkDetail::getPlanStartDateTime, task.getPlanStartDateTime());
            owd.set(ApsWorkDetail::getPlanEndDateTime, task.getPlanEndDateTime());
            owd.set(ApsWorkDetail::getStatus, 1); // 3 已排产

            owd.set(ApsWorkDetail::getGroupCode, detail.getGroupCode());
            owd.set(ApsWorkDetail::getGroupId, detail.getGroupId());
            owd.set(ApsWorkDetail::getStationId, detail.getStationId());

            owd.set(ApsWorkDetail::getTotalWorkTime, detail.getTotalWorkTime());
            owd.set(ApsWorkDetail::getEnterTime, detail.getEnterTime());
            owd.set(ApsWorkDetail::getOutTime, detail.getOutTime());
            owd.set(ApsWorkDetail::getChangTime, detail.getChangTime());

            apsWorkDetailMapper.update(null, owd);

            List<ApsWeekDayWrap> startMachineCalendar = task.getAll();
            for (ApsWeekDayWrap machineCalendar : startMachineCalendar) {
                ApsDetailWeekday calendar = new ApsDetailWeekday();

                calendar.setOrderId(detail.getOrderId());
                calendar.setApsOrderId(detail.getApsOrderId());
                calendar.setApsBomId(detail.getApsWorkOrderId());
                calendar.setPhaseId(detail.getPhaseId());
                calendar.setPhaseCode(detail.getPhaseCode());
                calendar.setPhaseName(detail.getPhaseName());
                calendar.setApsWorkOrderId(detail.getApsWorkOrderId());
                calendar.setApsWorkDetailId(detail.getId());
                calendar.setGroupId(detail.getGroupId());

                calendar.setWorkTime(machineCalendar.getSecond());
                calendar.setPlanStartDateTime(machineCalendar.getStart());
                calendar.setPlanEndDateTime(machineCalendar.getEnd());
                calendar.setShiftId(machineCalendar.getShiftId());
                calendar.setShiftSeq(machineCalendar.getShiftSeq());
                calendar.setShiftStartDateTime(machineCalendar.getShiftStartDateTime());
                calendar.setShiftEndDateTime(machineCalendar.getShiftEndDateTime());
                calendar.setShiftDay(machineCalendar.getShiftDay());
                calendar.setStationGroupId(detail.getStationGroupId());
                calendar.setStationId(detail.getStationId());
                calendar.setState(1); // 3 已排产
                calendar.setOrderType(apsOrderCache.getOrder().getOrderType());
                apsOrderCache.getDetailWeekdays().add(calendar);


            }
        }

        apsDetailWeekdayMapper.insertBatchSomeColumn(apsOrderCache.getDetailWeekdays());

    }

    @Transactional
    @Override
    public void scheduleWl(ApsFactoryCache apsFactoryCache, ApsOrderCache apsOrderCache) {

        // 根据ID分组
        Map<Integer, ApsWorkOrder> idMap = apsOrderCache.getWorkOrders().stream().collect(Collectors.toMap(ApsWorkOrder::getId, Function.identity()));
        for (ApsWorkOrder workOrder : apsOrderCache.getWorkOrders()) {
            if (!workOrder.getWorkChain().equals("WL")) {
                continue;
            }

//            if (workOrder.getPlmId().startsWith("010") || workOrder.getPlmId().startsWith("020") || workOrder.getPlmId().startsWith("050")) {
//                continue;
//            }

            ApsWorkOrder apsWorkOrder = idMap.get(workOrder.getPid());

            workOrder.setPlanStartDateTime(apsWorkOrder.getPlanStartDateTime());
            workOrder.setPlanEndDateTime(apsWorkOrder.getPlanStartDateTime());

            apsWorkOrderMapper.updateById(workOrder);
        }
    }

    @Override
    public String getCode() {
        return APS_FORWARD_SCHEDULING_CODE;
    }
}
